﻿using System;
using Grammars.Core;

namespace Grammars.Examples
{
    public static class ExpressionsGrammar
    {
        /// <summary>
        /// Represents an ExpressionNode used to evaluate inline expressions.
        /// </summary>
        public class ExpressionNode : Node
        {
            /// <summary>
            /// Holds a value associated with the current node.
            /// </summary>
            public double Value { get; set; }
        }

        /// <summary>
        /// Builds the expressions grammar and parser
        /// and enters the evaluation loop.
        /// </summary>
        private static void Run()
        {
            // Creating the Grammar instance
            var G = new Grammar<ExpressionNode>();

            // Rules
            var E = G.Start("E");
            var T = G.Rule("T");
            var F = G.Rule("F");

            // Tokens
            var PLUS = G.Token('+');
            var MINUS = G.Token('-');
            var MULTIPLY = G.Token('*');
            var DIVIDE = G.Token('/');
            var CONST = G.Token("CONST", @"\d+");
            var L = G.Token('(');
            var R = G.Token(')');

            // Productions
            // ** Notice that the semantic rules perform the evaluation during parsing

            // E ::= E + T | E - T | T | -E
            E %= (E + PLUS + T)
                     .With(x => x.Value = E.Node.Value + T.Node.Value) |
                 (E + MINUS + T)
                     .With(x => x.Value = E.Node.Value - T.Node.Value) |
                 (T)
                     .With(x => x.Value = T.Node.Value) |
                 (MINUS + E)
                     .With(x => x.Value = -E.Node.Value);

            // T ::= T * F | T/F | F
            T %= (T + MULTIPLY + F)
                     .With(x => x.Value = T.Node.Value * F.Node.Value) |
                 (T + DIVIDE + F)
                     .With(x => x.Value = T.Node.Value / F.Node.Value) |
                 (F)
                     .With(x => x.Value = F.Node.Value);

            // F ::= CONST | ( E )
            F %= (CONST)
                     .With(x => x.Value = Convert.ToDouble(CONST.Node.Match)) |
                 (L + E + R)
                     .With(x => x.Value = E.Node.Value);

            // Building an LR(1) parser
            var parser = Parsers.BuildLr(G);

            // Enter the evaluation loop
            while (true)
            {
                // Read the expression
                Console.WriteLine("Enter an expression to evaluate (empty to exit):");
                string exp = Console.ReadLine();

                if (string.IsNullOrEmpty(exp))
                    break;

                // Parse (and evaluate) the expression
                var node = parser.ParseString(exp);

                // Print the result
                Console.WriteLine(node.Value);
            }
        }
    }
}
